//
//  HPUserService.m
//  LockTest
//
//  Created by ZhangBo on 2018/12/2.
//  Copyright © 2018 ZhangBo. All rights reserved.
//

#import "HPUserService.h"
#import "HPUser.h"
#import "HPUserBuilder.h"
#import "HPAlbumService.h"
#import "HPUserService.h"
#import "HPSyncService.h"

#import <ReactiveObjC/ReactiveObjC.h>

@interface HPUserService ()
// 服务还持有了自己所创建的用户对象的缓存
@property(nonatomic, strong) NSMutableDictionary *userCache;
@property(nonatomic, strong) HPUserBuilder *userBuilder;
@end

@implementation HPUserService

//初始化器被复写来初始化缓存
- (instancetype)init {
    if (self = [super init]) {
        self.userCache = [NSMutableDictionary dictionary];
        self.userBuilder = [HPUserBuilder new];
    }
    return self;
}

+ (instancetype)sharedInstance {
    static HPUserService *instance = nil;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        instance = [[HPUserService alloc] init];
    });
    return instance;
}

//  使用 userWithId:completion: 方法可以获取有给定id的用户。
//  如果该对象存在于本地状态，则会被返回。否则，它会请求服务器获取详细信息。
//  一旦完成，completion回调会被触发，以通知调用者对象可用。
- (void)userWithId:(NSString *)uid completion:(void (^)(HPUser *))completion {
    //检查本地缓存或从服务器提取
    HPUser *user = (HPUser *) self.userCache[uid];
    if (user) {
        NSLog(@"从本地缓存来获取");
        completion(user);
    } else {
        NSLog(@"本地没有,需要从服务器来获取");
    }
    //  假设这里存在一个同步服务HPSyncService。该服务会从服务器获取数据。
    //  还假设服务器发送了JSON对象，并被反序列化为NSDictionary对象。
    //  抽取属性和填充生成器的代码被忽略了。一旦数据可用，我们还将更新本地的缓存，以避免后续的服务器请求。
    [[HPSyncService sharedInstance] fetchType:@"user" userid:uid completion:^(NSDictionary *data) {
//        //使用HPUserBuilder，分析数据并构建
//        HPUser *userFromServer = [self.userBuilder build];
        HPUser *userFromServer = [[HPUser alloc] initWithDictionary:data];
        self.userCache[userFromServer.userId] = userFromServer;
        completion(userFromServer);
    }];
}

//使用 updateUser:completion: 方法可以更新用户的状态。
- (void)updateUser:(HPUser *)user completion:(void (^)(HPUser *))completion {
    //可能会要求更新到服务器
    [[HPSyncService sharedInstance] updateType:@"user" completion:^(NSDictionary *data) {
        //使用HPUserBuilder，分析数据并构建
//        HPUser *updatedUser = [self.userBuilder build];
        HPUser *updatedUser = [[HPUser alloc] initWithDictionary:data];
        updatedUser.albums = user.albums;
        //更新本地状态可能需要同步修改服务器。
        self.userCache[updatedUser.userId] = updatedUser;
        //一旦通知了服务器，本地缓存就要被更新。因为用户对象持有相册，所以相册服务也用于更新相关的相册。
        [HPAlbumService updateAlbums:updatedUser.albums completion:^{
            completion(updatedUser);
        }];
    }];
}

- (RACSignal *)signalForUserWithId:(NSString *)uid {
    @weakify(self)
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        @strongify(self)
        HPUser *userFromCache = self.userCache[uid];
        if (userFromCache) {
            [subscriber sendNext:userFromCache];
            [subscriber sendCompleted];

        } else {
            //假设HPSyncService也遵循FRP风格
            RACSignal *racs = [[HPSyncService sharedInstance] loadType:@"user" withId:uid];
            [racs subscribeNext:^(HPUser *userFromServer) {

                //也更新本地缓存和通知
                [subscriber sendNext:userFromServer];
                [subscriber sendCompleted];
            }             error:^(NSError *error) {
                [subscriber sendError:error];
            }];

        }
        NSLog(@"创建一个信号");
        return nil;
    }];
    return signal;

}

- (RACSignal *)signalForUpdateUser:(HPUser *)user {
    // signalForUpdateUser 方法更新一个 HPUser 对象
    //这里创建了 RACSignal。
    @weakify(self);
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
        //        trongifer(strongSelf);
        RACSignal *racs = [[HPSyncService sharedInstance] updateType:@"user" withId:user.userId value:user];
        [racs subscribeNext:^(NSDictionary *data) {
            //使用HPUserBuilder，分析数据并构建
            HPUser *updatedUser = [self.userBuilder build];
            @strongify(self)
            id oldUser = self.userCache[updatedUser.userId];
            self.userCache[updatedUser.userId] = updatedUser;
            [subscriber sendNext:updatedUser];
            [subscriber sendCompleted];
            //当用户被更新，你不仅需要通知直接订阅者，还要通知观察者更新缓存。
            [self notifyCacheUpdatedWithUser:updatedUser old:oldUser];
        }             error:^(NSError *_Nullable error) {
            [subscriber sendError:error];
        }];
        NSLog(@"创建一个信号");
        return nil;
    }];
    return signal;
}

//notifyCacheUpdatedWithUser:old: 广播了用户对象的变化
- (void)notifyCacheUpdatedWithUser:(HPUser *)user old:(HPUser *)oldUser {
    NSDictionary *tuple = @{
        @"old": oldUser,
        @"new": user
    };
    //这里使用 NSNotificationCenter 是为了简化。这个方法不宜暴露给 HPUserService 类的用户。这是个扩展方法。
    [NSNotificationCenter.defaultCenter postNotificationName:@"userUpdated" object:tuple];
}


- (RACSignal *)signalForUserUpdates:(id)object {
    //这里使用了ReactiveCocoa框架提供的分类扩展rac_addObserverForName，以订阅 userUpdated 通知。它也会实际从 NSNotification 中抽取 NSDictionary，后者由旧的和 新的用户对象所组成。
    return [[NSNotificationCenter.defaultCenter rac_addObserverForName:@"userUpdated" object:object] flattenMap:^(NSNotification *note) {
        return note.object;
    }];
}

@end
